package com.cwz.file.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cwz.api.entity.UserFile;
import com.cwz.api.entity.UserPath;
import com.cwz.core.constant.HttpStatus;
import com.cwz.core.result.R;
import com.cwz.core.utils.date.DateUtils;
import com.cwz.core.utils.string.StringUtils;
import com.cwz.core.utils.uuid.Seq;
import com.cwz.file.config.FileProperties;
import com.cwz.file.config.MinioProperties;
import com.cwz.file.mapper.UserFileMapper;
import com.cwz.file.mapper.UserPathMapper;
import com.cwz.file.service.UserFileService;
import com.cwz.file.utils.MinioUtils;
import io.minio.MinioClient;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.*;

/**
 * <p>
 * 用户文件表 服务实现类
 * </p>
 *
 * @author Wen
 * @since 2023-06-28
 */
@Service
public class UserFileServiceImpl extends ServiceImpl<UserFileMapper, UserFile> implements UserFileService {
	@Autowired
	private UserPathMapper userPathMapper;
	@Autowired
	private UserFileMapper userFileMapper;
	@Autowired
	private FileProperties fileProperties;
	@Autowired
	private MinioProperties minioProperties;
	@Autowired
	private MinioClient minioClient;

	@Override
	public R<Map<String, Object>> upload(MultipartFile[] files, Long pathId) {
		Map<String, Object> map = new HashMap<>();
		try {
			UserPath userPath = this.userPathMapper.selectById(pathId);
			// 查询该文件夹前面是否还有文件夹
			List<String> catalogue = new ArrayList<>();
			this.getCatalogue(userPath.getFolderName(), userPath.getPid(), catalogue);
			for (MultipartFile file : files) {
				R<Map<String, Object>> mapR = MinioUtils.checkFile(file, this.fileProperties.getMaxSize(), this.fileProperties.getTypes());
				if (mapR.getCode() == HttpStatus.ERROR) {
					map.put(file.getOriginalFilename(), mapR.getMsg());
					continue;
				}
				Map<String, Object> data = mapR.getData();
				String fileType = (String) data.get("fileType");
				double fileSize = (double) data.get("fileSize");
				String fileName = file.getOriginalFilename().split("\\.")[0] + "." + fileType;
				String url = MinioUtils.putObject(minioClient,
					this.minioProperties.getFileBucket(),
					"user_id_" + userPath.getUserId() + "/" + String.join("/", catalogue) + "/" + extractFilename(file, fileType),
					file);
				url = URLDecoder.decode(url, "utf-8");
				UserFile userFile = new UserFile();
				userFile.setPathId(pathId);
				userFile.setFileName(StringUtils.substring(fileName, 0, 255));
				userFile.setFileType(fileType);
				userFile.setFileSize(fileSize);
				userFile.setFilePath(url);
				userFile.setDownloadPath(StringUtils.remove(url, this.minioProperties.getUrl() + "/" + this.minioProperties.getFileBucket() + "/").split("\\?")[0]);
				this.userFileMapper.insert(userFile);
				map.put(file.getOriginalFilename(), "上传文件成功");
			}
			return R.ok(map);
		} catch (Exception e) {
			e.printStackTrace();
			return R.fail("上传文件失败");
		}
	}

	@Override
	public void download(Long fileId, HttpServletResponse response) {
		UserFile userFile = this.userFileMapper.selectById(fileId);
		InputStream stream = null;
		try {
			stream = MinioUtils.getObject(minioClient, this.minioProperties.getFileBucket(), userFile.getDownloadPath());
			response.setContentType("application/force-download");
			String fileName = URLEncoder.encode(userFile.getFileName(), "UTF-8").replaceAll("\\+", "%20");
			response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
			response.setCharacterEncoding("UTF-8");
			BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
			byte[] by = new byte[32768];
			int index = stream.read(by, 0, 32768);
			while (index != -1) {
				out.write(by, 0, index);
				index = stream.read(by, 0, 32768);
			}
			out.flush();
			response.flushBuffer();
		} catch (IOException e1) {
			e1.printStackTrace();
		} catch (Exception e) {
			log.error(e.getMessage());
		} finally {
			try {
				response.getOutputStream().close();
				stream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public R<Void> deleteFile(Long fileId) {
		UserFile userFile = this.userFileMapper.selectById(fileId);
		if (this.userFileMapper.deleteById(fileId) > 0) {
			MinioUtils.remObject(minioClient, this.minioProperties.getFileBucket(), userFile.getDownloadPath());
			return R.ok("删除文件成功");
		}
		return R.fail("删除文件失败");
	}

	@Override
	public R<Map<String, Object>> listFile(Long pathId, String fileName, String fileType, Integer pageNum, Integer pageSize) {
		Page<UserFile> page = this.userFileMapper.selectPage(new Page<>(pageNum, pageSize),
			new QueryWrapper<UserFile>()
				.eq("path_id", pathId)
				.like(StringUtils.isNotBlank(fileName), "file_name", fileName)
				.like(StringUtils.isNotBlank(fileType), "file_type", fileType)
				.orderByAsc("file_id"));
		Map<String, Object> result = new HashMap<>();
		result.put("total", page.getTotal());
		result.put("records", page.getRecords());
		return R.ok(result);
	}

	@Override
	public R<String> shareFile(Long fileId) {
		String filePath = this.userFileMapper.selectById(fileId).getFilePath();
		return R.ok(filePath);
	}

	private void getCatalogue(String folderName, Long pid, List<String> catalogue) {
		if (pid == 0) {
			catalogue.add(folderName);
			return;
		}
		UserPath userPath = this.userPathMapper.selectById(pid);
		this.getCatalogue(userPath.getFolderName(), userPath.getPid(), catalogue);
		catalogue.add(folderName);
	}

	/**
	 * @Description: 编码文件名
	 * @Author: Wen
	 * @param file:
	 * @param fileType:
	 * @return: java.lang.String
	 **/
	private String extractFilename(MultipartFile file, String fileType) {
		return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
			FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), fileType);
	}
}
